{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# C++ 运算符重载"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. 运算符重载简介"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "① 运算符重载：对已有的运算符重新进行定义，赋予其另一种功能，以适应不同的数据类型。\n",
    "\n",
    "② 对于内置的数据类型的表达式的运算符是不可能改变的。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. 加号运算符重载"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "① 加号运算符作用：实现两个自定义数据类型相加的运算。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "using namespace std;\n",
    "\n",
    "//加号运算符重载\n",
    "class Person\n",
    "{\n",
    "public:\n",
    "\n",
    "    //1、成员函数重载+号\n",
    "    Person operator+(Person& p)\n",
    "    {\n",
    "        Person temp;\n",
    "        temp.m_A = this->m_A + p.m_A;\n",
    "        temp.m_B = this->m_B + p.m_B;\n",
    "        return temp;\n",
    "    }\n",
    "    int m_A;\n",
    "    int m_B;\n",
    "};\n",
    "\n",
    "/*\n",
    "//全局函数重载+号\n",
    "Person operator+(Person &p1, Person &p2)\n",
    "{\n",
    "    Person temp;\n",
    "    temp.m_A = p1.m_A + p2.m_A;\n",
    "    temp.m_B = p1.m_B + p2.m_B;\n",
    "    return temp;\n",
    "}\n",
    "*/\n",
    "\n",
    "//函数重载的版本\n",
    "Person operator+(Person& p1, int num)\n",
    "{\n",
    "    Person temp;\n",
    "    temp.m_A = p1.m_A + num;\n",
    "    temp.m_B = p1.m_B + num;\n",
    "    return temp;\n",
    "}\n",
    "\n",
    "void test01()\n",
    "{\n",
    "    Person p1;\n",
    "    p1.m_A = 10;\n",
    "    p1.m_B = 10;\n",
    "    Person p2;\n",
    "    p2.m_A = 10;\n",
    "    p2.m_B = 10;\n",
    "\n",
    "    //成员函数重载本质调用 \n",
    "    //Person p3 = p1.operator+(p2);\n",
    "    \n",
    "    //全局函数重载本质调用 \n",
    "    //Person p3 = operator+(p1,p2);\n",
    "    \n",
    "    Person p3 = p1 + p2; //重载本质被简化后的形式\n",
    "\n",
    "    //运算符重载，也可以发生函数重载\n",
    "\n",
    "    Person p4 = p1 + 10; //Person + int\n",
    "\n",
    "    cout << \"p3.m_A：\" << p3.m_A << endl;\n",
    "    cout << \"p3.m_B：\" << p3.m_B << endl;\n",
    "\n",
    "    cout << \"p4.m_A：\" << p4.m_A << endl;\n",
    "    cout << \"p4.m_B：\" << p4.m_B << endl;\n",
    "\n",
    "}\n",
    "\n",
    "\n",
    "int main()\n",
    "{\n",
    "    test01();\n",
    "\n",
    "    system(\"pause\");\n",
    "\n",
    "    return 0;\n",
    "\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果：  \n",
    " - p3.m_A：20  \n",
    " - p3.m_B：20  \n",
    " - p4.m_A：20  \n",
    " - p4.m_B：20  \n",
    " - 请按任意键继续. . ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. 左移运算符重载"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "① 左移运算符重载：可以输出自定义数据类型。\n",
    "\n",
    "② 重载左移运算符配合友元可以实现自定义数据类型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "using namespace std;\n",
    "\n",
    "//左移运算符\n",
    "class Person\n",
    "{\n",
    "\n",
    "    friend ostream& operator<<(ostream& out, Person& p);\t\n",
    "\n",
    "public:\n",
    "    Person(int a, int b)\n",
    "    {\n",
    "        m_A = a;\n",
    "        m_B = b;\n",
    "    }\n",
    "\n",
    "    /*\n",
    "    //利用成员函数重载 左移运算符  p.operator<<(cout) 简化版本 p << cout\n",
    "    //成员函数不能利用重载<<运算符，因为无法实现cout在左侧\n",
    "    //当不知道返回值是什么类型的时候，可以先写个void，以后再改\n",
    "    void operator<<(Person& p)\n",
    "    {\n",
    "        \n",
    "    }\n",
    "    */\n",
    "    \n",
    "private:\n",
    "    int m_A;\n",
    "    int m_B;\n",
    "};\n",
    "\n",
    "//只能利用全局函数重载左移运算符\n",
    "//如果返回类型为void，那么就无法无限追加，也没有办法在后面添加换行符\n",
    "ostream & operator<<(ostream &cout, Person &p) //本质 operator << (cout , p) , 简化 cout << p\n",
    "                                               //cout是别名，这里可以取out、kn...\n",
    "                                               //cout为ostream输出流数据类型                                                    \n",
    "{\n",
    "    cout << \"m_A= \" << p.m_A << \" m_B=\" << p.m_B;\n",
    "    return cout;\n",
    "}\n",
    "\n",
    "void test01()\n",
    "{\n",
    "    Person p(10,10);\n",
    "\n",
    "    cout << p << \" hello world\" << endl;\n",
    "}\n",
    "\n",
    "\n",
    "int main()\n",
    "{\n",
    "    test01();\n",
    "\n",
    "    system(\"pause\");\n",
    "\n",
    "    return 0;\n",
    "\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果：  \n",
    " - m_A= 10 m_B=10 hello world  \n",
    " - 请按任意键继续. . ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4. 递增运算符"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "① 递增运算符重载：通过重载递增运算符，实现自己的整型数据。\n",
    "\n",
    "② 前置递增返回的是引用，后置递增返回的是值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "using namespace std;\n",
    "\n",
    "//重载递增运算符\n",
    "\n",
    "class MyInteger\n",
    "{\n",
    "    friend ostream& operator<<(ostream& cout, MyInteger myint);\n",
    "\n",
    "public:\n",
    "    MyInteger()\n",
    "    {\n",
    "        m_Num = 0;\n",
    "    }\n",
    "    \n",
    "    //重载前置++运算符,返回引用是为了一直对一个数据进行递增操作,而返回值并不是一直对一个数据进行递增操作                    \n",
    "    MyInteger& operator++()\n",
    "    {\n",
    "        //先进行++运算\n",
    "        m_Num++;\n",
    "\n",
    "        //再将自身做一个返回\n",
    "        return *this;  //把自身做一个返回\n",
    "    }\n",
    "\n",
    "    //重载后置++运算符 int代表占位参数，可以用于区分前置和后置递增\n",
    "    //后置递增不能返回引用，因为temp是局部变量，如果返回temp，当程序运行完后变量就释放了，再调用temp就是非法操作了           \n",
    "    MyInteger operator++(int)\n",
    "    {\n",
    "        //先记录当时结果\n",
    "        MyInteger temp = *this;\n",
    "        //后 递增\n",
    "        m_Num++;\n",
    "        //最后将记录结果做返回\n",
    "        return temp;\n",
    "    }\n",
    "private:\n",
    "    int m_Num;\n",
    "};\n",
    "\n",
    "//只能利用全局函数重载左移运算符\n",
    "ostream & operator<<(ostream &cout, MyInteger myint) //本质 operator << (cout , p) , 简化 cout << p\n",
    "                                                     //cout是别名，这里可以取out、kn...\n",
    "                                                     //cout为ostream输出流数据类型                                               \n",
    "{\n",
    "    cout << myint.m_Num;\n",
    "\n",
    "    return cout;\n",
    "}\n",
    "\n",
    "void test01()\n",
    "{\n",
    "    MyInteger myint;\n",
    "\n",
    "    cout << ++(++myint) << endl;\n",
    "    cout << myint << endl;\n",
    "}\n",
    "\n",
    "void test02()\n",
    "{\n",
    "    MyInteger myint;\n",
    "\n",
    "    cout << myint++ << endl;\n",
    "    cout << myint   << endl;\n",
    "\n",
    "}\n",
    "\n",
    "int main()\n",
    "{\n",
    "    test01();\n",
    "    test02();\n",
    "\n",
    "    /*\n",
    "    int a = 0;\n",
    "    cout << ++(++a) << endl; //运行结果为2\n",
    "    cout << a << endl;       //运行结果为2，表示一直对一个数据进行递增\n",
    "    */\n",
    "\n",
    "    system(\"pause\");\n",
    "\n",
    "    return 0;\n",
    "\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果：  \n",
    " - 2  \n",
    " - 2  \n",
    " - 0  \n",
    " - 1  \n",
    " - 请按任意键继续. . ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5. 赋值运算符"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "① C++编译器至少给一个类添加4个函数：\n",
    "\n",
    "1. 默认构造函数(无参，函数体为空)\n",
    "2. 默认析构函数(无参，函数体为空)\n",
    "3. 默认拷贝构造函数，对属性进行值拷贝\n",
    "4. 赋值运算符operator=，对属性进行值拷贝\n",
    "5. 如果类中有属性指向堆区，做赋值操作时也会出现浅拷贝问题。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "using namespace std;\n",
    "\n",
    "//重载赋值运算符\n",
    "\n",
    "class Person\n",
    "{\n",
    "public:\n",
    "    Person(int age)\n",
    "    {\n",
    "        m_Age = new int(age);\n",
    "    }\n",
    "    \n",
    "    ~Person()\n",
    "    {\n",
    "        if (m_Age != NULL)\n",
    "        {\n",
    "            delete m_Age;\n",
    "            m_Age = NULL;\n",
    "        }\n",
    "    }\n",
    "\n",
    "    //重载 赋值运算符\n",
    "    //如果返回的是值，而不是引用，是创建一个拷贝函数，返回的是一个副本，而不是自身\n",
    "    Person& operator=(Person& p)\n",
    "    {\n",
    "        //编译器默认是提供浅拷贝\n",
    "        //m_Age = p.m_Age;\n",
    "\n",
    "        //浅拷贝带来的问题是，当创建数据在堆区时，析构代码导致内存重复释放，报错\n",
    "\n",
    "        //应该先判断是否有属性在堆区，如果有先释放干净，然后再深拷贝\n",
    "        if (m_Age != NULL)\n",
    "        {\n",
    "            delete m_Age;\n",
    "            m_Age = NULL;\n",
    "        }\n",
    "\n",
    "        //深拷贝\n",
    "        m_Age = new int(*p.m_Age);\n",
    "\n",
    "        //返回对象本身\n",
    "        return *this;\n",
    "    }\n",
    "\n",
    "    int *m_Age;\n",
    "};\n",
    "\n",
    "void test01()\n",
    "{\n",
    "    Person p1(18);\n",
    "\n",
    "    Person p2(20);\n",
    "\n",
    "    Person p3(23);\n",
    "\n",
    "    p3 = p2 = p1; //赋值操作\n",
    "\n",
    "    cout << \"p1的年龄为：\" << *p1.m_Age << endl;\n",
    "\n",
    "    cout << \"p2的年龄为：\" << *p2.m_Age << endl;\n",
    "\n",
    "    cout << \"p3的年龄为：\" << *p3.m_Age << endl;\n",
    "\n",
    "\n",
    "}\n",
    "\n",
    "int main()\n",
    "{\n",
    "    test01();\n",
    "\n",
    "    /*\n",
    "    \n",
    "    int a = 10;\n",
    "    int b = 20;\n",
    "    int c = 30;\n",
    "    \n",
    "    c = b = a;\n",
    "\n",
    "    cout << \"a= \" << a << endl;\n",
    "    cout << \"b= \" << b << endl;\n",
    "    cout << \"c= \" << c << endl;\n",
    "    */\n",
    "\n",
    "    system(\"pause\");\n",
    "\n",
    "    return 0;\n",
    "\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果：  \n",
    " - p1的年龄为：18  \n",
    " - p2的年龄为：18  \n",
    " - p3的年龄为：18  \n",
    " - 请按任意键继续. . ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 6. 关系重载运算符"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "using namespace std;\n",
    "#include<string>\n",
    "\n",
    "//重载关系运算符\n",
    "\n",
    "class Person\n",
    "{\n",
    "public:\n",
    "    Person(string name, int age)\n",
    "    {\n",
    "        m_Name = name;\n",
    "        m_Age = age;\n",
    "    }\n",
    "\n",
    "    //重载 == 号\n",
    "    bool operator==(Person& p)\n",
    "    {\n",
    "        if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)\n",
    "        {\n",
    "            return true;\n",
    "        }\n",
    "        return false;\n",
    "    }\n",
    "\n",
    "    bool operator!=(Person& p)\n",
    "    {\n",
    "        if (this->m_Name != p.m_Name && this->m_Age != p.m_Age)\n",
    "        {\n",
    "            return true;\n",
    "        }\n",
    "        return false;\n",
    "    }\n",
    "\n",
    "    string m_Name;\n",
    "    int m_Age;\n",
    "};\n",
    "\n",
    "void test01()\n",
    "{\n",
    "    Person p1(\"Tom\", 17);\n",
    "\n",
    "    Person p2(\"Jerry\", 18);\n",
    "\n",
    "    if (p1 == p2)\n",
    "    {\n",
    "        cout << \"p1和p2是相等的！\" << endl;\n",
    "    }\n",
    "    else\n",
    "    {\n",
    "        cout << \"p1和p2是不相等的！\" << endl;\n",
    "\n",
    "    }\n",
    "\n",
    "    if (p1 != p2)\n",
    "    {\n",
    "        cout << \"p1和p2是不相等的！\" << endl;\n",
    "    }\n",
    "    else\n",
    "    {\n",
    "        cout << \"p1和p2是相等的！\" << endl;\n",
    "\n",
    "    }\n",
    "\n",
    "\n",
    "}\n",
    "\n",
    "int main()\n",
    "{\n",
    "    test01();\n",
    "    \n",
    "    system(\"pause\");\n",
    "\n",
    "    return 0;\n",
    "\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果：  \n",
    " - p1和p2是不相等的！  \n",
    " - p1和p2是不相等的！  \n",
    " - 请按任意键继续. . ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 7. 函数调用运算符重载"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "① 函数调用运算符()也可以重载。\n",
    "\n",
    "② 由于重载后使用的方式非常像函数的调用，因此称为仿函数。\n",
    "\n",
    "③ 仿函数没有固定写法，非常灵活。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "using namespace std;\n",
    "#include<string>\n",
    "\n",
    "//函数调用运算符重载\n",
    "\n",
    "//打印输出类\n",
    "class MyPrint\n",
    "{\n",
    "public:\n",
    "\n",
    "    //重载函数调用运算符\n",
    "    void operator()(string test)\n",
    "    {\n",
    "        cout << test << endl;\n",
    "    }\n",
    "\n",
    "};\n",
    "\n",
    "//正常函数\n",
    "void MyPrint02(string test)\n",
    "{\n",
    "    cout << test << endl;\n",
    "}\n",
    "\n",
    "void test01()\n",
    "{\n",
    "    MyPrint myPrint;\n",
    "\n",
    "    myPrint(\"hello world\");\n",
    "\n",
    "}\n",
    "\n",
    "//仿函数非常灵活，没有固定的写法\n",
    "//加法类\n",
    "class MyAdd\n",
    "{\n",
    "public:\n",
    "    int operator()(int num1, int num2)\n",
    "    {\n",
    "        return num1 + num2;\n",
    "    }\n",
    "};\n",
    "\n",
    "void test02()\n",
    "{\n",
    "    MyAdd myadd;\n",
    "    int ret = myadd(100,100);\n",
    "    cout << \"ret = \" << ret << endl;\n",
    "\n",
    "    //匿名函数对象\n",
    "    cout << MyAdd()(100, 100) << endl;\n",
    "\n",
    "    // MyAdd()为创建一个匿名对象，匿名对象的特点为当前行执行完立即释放\n",
    "}\n",
    "\n",
    "int main()\n",
    "{\n",
    "    test01();\n",
    "\n",
    "    MyPrint02(\"hello world\"); //由于使用起来非常类似于函数调用，因此称为仿函数\n",
    "\n",
    "    test02();\n",
    "\n",
    "    system(\"pause\");\n",
    "\n",
    "    return 0;\n",
    "\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果：  \n",
    " - hello world  \n",
    " - hello world  \n",
    " - ret = 200  \n",
    " - 200  \n",
    " - 请按任意键继续. . ."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.6.3",
   "language": "python",
   "name": "python3.6.3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
